package webclient;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;

/**
 * A graphical client that lets you interactively connect to Web servers and
 * send custom URLs, request headers, and query data. The user can optionally
 * select a GET or POST request and HTTP version 1.0 or 1.1.
 * <P>
 * For an HTTPS connection, you can specify a nondefault keystore through system
 * properties on the command line, i.e.,
 * <P>
 * java -Djavax.net.ssl.trustStore=server.ks -Djavax.net.ssl.trustStoreType=JKS
 * <P>
 * Taken from Core Servlets and JavaServer Pages Volume II from Prentice Hall
 * and Sun Microsystems Press, http://volume2.coreservlets.com/. (C) 2007 Marty
 * Hall, Larry Brown, and Yaakov Chaikin; may be freely used or adapted.
 */

public class WebClient extends JPanel implements Runnable {
	public static void main(String[] args) {
		if (args.length > 0) {
			usage();
		} else {
			try {
				UIManager.setLookAndFeel(UIManager
						.getSystemLookAndFeelClassName());
			} catch (Exception e) {
				System.out.println("Error setting native LAF: " + e);
			}
			Container content = new WebClient();
			content.setBackground(SystemColor.control);
			JFrame frame = new JFrame("Web Client");
			frame.setContentPane(content);
			frame.setBackground(SystemColor.control);
			frame.setSize(600, 700);
			frame.setLocationRelativeTo(null);
			frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
			frame.setVisible(true);
		}
	}

	private static JFrame frame;
	private LabeledTextField urlField;
	private JComboBox methodCombo, versionCombo;
	private LabeledTextField proxyHostField, proxyPortField;
	private JTextArea requestHeadersArea, queryDataArea;
	private JTextArea resultArea;
	private JButton encodeButton, submitButton, interruptButton;
	private Font labelFont, headingFont, textFont;
	private HttpClient client;

	public WebClient() {
		int fontSize = 14;
		labelFont = new Font("Serif", Font.BOLD, fontSize);
		headingFont = new Font("SansSerif", Font.BOLD, fontSize + 4);
		textFont = new Font("Monospaced", Font.BOLD, fontSize - 2);
		setLayout(new BorderLayout(5, 30));
		// Set up URL, Request Method, and Proxy.
		JPanel topPanel = new JPanel(new GridLayout(3, 1));
		topPanel.add(getURLPanel());
		topPanel.add(getRequestMethodPanel());
		topPanel.add(getProxyPanel());
		// Set up Request Header and Query Data.
		JPanel inputPanel = new JPanel(new GridLayout(3, 1));
		inputPanel.add(topPanel);
		inputPanel.add(getRequestHeaderPanel());
		inputPanel.add(getQueryDataPanel());
		add(inputPanel, BorderLayout.NORTH);
		add(getResultPanel(), BorderLayout.CENTER);
	}

	private JPanel getURLPanel() {
		JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		urlField = new LabeledTextField("URL:", labelFont, 75, textFont);
		panel.add(urlField);
		return (panel);
	}

	private JPanel getRequestMethodPanel() {
		JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JLabel methodLabel = new JLabel(" Request Method:");
		methodLabel.setFont(labelFont);
		panel.add(methodLabel);
		methodCombo = new JComboBox();
		methodCombo.addItem("GET");
		methodCombo.addItem("POST");
		panel.add(methodCombo);
		JLabel versionlabel = new JLabel(" HTTP Version:");
		versionlabel.setFont(labelFont);
		panel.add(versionlabel);
		versionCombo = new JComboBox();
		versionCombo.addItem("HTTP/1.0");
		versionCombo.addItem("HTTP/1.1");
		panel.add(versionCombo);
		return (panel);
	}

	private JPanel getProxyPanel() {
		JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		proxyHostField = new LabeledTextField("Proxy Host:", labelFont, 35,
				textFont);
		proxyPortField = new LabeledTextField("Proxy Port:", labelFont, 5,
				textFont);
		panel.add(proxyHostField);
		panel.add(proxyPortField);
		// Check to see if command-line system properties are set
		// for proxy.
		String proxyHost = System.getProperty("http.proxyHost");
		String sslProxyHost = System.getProperty("https.proxyHost");
		String proxyPort = System.getProperty("http.proxyPort");
		String sslProxyPort = System.getProperty("https.proxyPort");
		if (proxyHost != null) {
			proxyHostField.setText(proxyHost);
			if (proxyPort != null) {
				proxyPortField.setText(proxyPort);
			}
		} else if (sslProxyHost != null) {
			proxyHostField.setText(sslProxyHost);
			if (sslProxyPort != null) {
				proxyPortField.setText(sslProxyPort);
			}
		}
		return (panel);
	}

	private JPanel getRequestHeaderPanel() {
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		JLabel requestLabel = new JLabel("Request Headers:");
		requestLabel.setFont(labelFont);
		panel.add(requestLabel, BorderLayout.NORTH);
		requestHeadersArea = new JTextArea(5, 80);
		requestHeadersArea.setFont(textFont);
		JScrollPane headerScrollArea = new JScrollPane(requestHeadersArea);
		panel.add(headerScrollArea, BorderLayout.CENTER);
		return (panel);
	}

	private JPanel getQueryDataPanel() {
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		JLabel formLabel = new JLabel("Query Data:");
		formLabel.setFont(labelFont);
		panel.add(formLabel, BorderLayout.NORTH);
		queryDataArea = new JTextArea(3, 80);
		queryDataArea.setFont(textFont);
		JScrollPane formScrollArea = new JScrollPane(queryDataArea);
		panel.add(formScrollArea, BorderLayout.CENTER);
		panel.add(getButtonPanel(), BorderLayout.SOUTH);
		return (panel);
	}

	private JPanel getButtonPanel() {
		JPanel panel = new JPanel();
		encodeButton = new JButton("Encode Data");
		encodeButton.addActionListener(new EncodeListener());
		encodeButton.setFont(labelFont);
		panel.add(encodeButton);
		submitButton = new JButton("Submit Request");
		submitButton.addActionListener(new SubmitListener());
		submitButton.setFont(labelFont);
		panel.add(submitButton);
		return (panel);
	}

	private JPanel getResultPanel() {
		JPanel resultPanel = new JPanel();
		resultPanel.setLayout(new BorderLayout());
		JLabel resultLabel = new JLabel("Results", JLabel.CENTER);
		resultLabel.setFont(headingFont);
		resultPanel.add(resultLabel, BorderLayout.NORTH);
		resultArea = new JTextArea();
		resultArea.setFont(textFont);
		JScrollPane resultScrollArea = new JScrollPane(resultArea);
		resultPanel.add(resultScrollArea, BorderLayout.CENTER);
		JPanel interruptPanel = new JPanel();
		interruptButton = new JButton("Interrupt Download");
		interruptButton.setFont(labelFont);
		interruptButton.addActionListener(new InterruptListener());
		interruptPanel.add(interruptButton);
		resultPanel.add(interruptPanel, BorderLayout.SOUTH);
		return (resultPanel);
	}

	/**
	 * Create all inputs and then process the request either directly
	 * (HttpClient) or through a proxy server (HttpProxyClient).
	 */

	public void run() {
		if (hasLegalValues()) {
			URL url = getRequestURL();
			String requestMethod = getRequestMethod();
			String httpVersion = getHttpVersion();
			ArrayList requestHeaders = getRequestHeaders();
			String queryData = getQueryData();
			resultArea.setText("");
			if (usingProxy()) {
				URL proxyURL = getProxyURL();
				client = new HttpProxyClient(url, proxyURL, requestMethod,
						httpVersion, requestHeaders, queryData, resultArea);
			} else {
				client = new HttpClient(url, requestMethod, httpVersion,
						requestHeaders, queryData, resultArea);
			}
			client.connect();
		}
	}

	public boolean usingProxy() {
		String proxyHost = getProxyHost();
		return (proxyHost != null && proxyHost.length() > 0);
	}

	private boolean hasLegalValues() {
		if (getRequestURL() == null) {
			report("Malformed URL");
			return (false);
		}
		if (usingProxy() && getProxyURL() == null) {
			report("Proxy invalid");
			return (false);
		}
		return (true);
	}

	// Turn proxy host and port into a URL.

	private URL getProxyURL() {
		URL requestURL = getRequestURL();
		if (requestURL == null) {
			return (null);
		}
		String proxyURLStr = requestURL.getProtocol() + "://" + getProxyHost();
		String proxyPort = getProxyPort();
		if (proxyPort != null && proxyPort.length() > 0) {
			proxyURLStr += ":" + proxyPort + "/";
		}
		return (getURL(proxyURLStr));
	}

	public URL getRequestURL() {
		return (getURL(urlField.getTextField().getText().trim()));
	}

	public URL getURL(String str) {
		try {
			URL url = new URL(str);
			return (url);
		} catch (MalformedURLException mue) {
			return (null);
		}
	}

	private String getRequestMethod() {
		return ((String) methodCombo.getSelectedItem());
	}

	private String getHttpVersion() {
		return ((String) versionCombo.getSelectedItem());
	}

	private String getProxyHost() {
		return (proxyHostField.getTextField().getText().trim());
	}

	private String getProxyPort() {
		return (proxyPortField.getTextField().getText().trim());
	}

	private ArrayList getRequestHeaders() {
		ArrayList requestHeaders = new ArrayList();
		int headerNum = 0;
		String header = requestHeadersArea.getText().trim();
		StringTokenizer tok = new StringTokenizer(header, "\r\n");
		while (tok.hasMoreTokens()) {
			requestHeaders.add(tok.nextToken());
		}
		return (requestHeaders);
	}

	private String getQueryData() {
		return (queryDataArea.getText());
	}

	private void report(String s) {
		resultArea.setText(s);
	}

	private static void usage() {
		System.out.println("Usage: java [-Djavax.net.ssl.trustStore=value] \n"
				+ "            [-Djavax.net.ssl.trustStoreType=value] \n"
				+ "            [-Dhttp.proxyHost=value] \n"
				+ "            [-Dhttp.proxyPort=value] \n"
				+ "            [-Dhttps.proxyHost=value] \n"
				+ "            [-Dhttps.proxyPort=value] WebClient");
	}

	/**
	 * Listener for Submit button. Performs HTTP request on separate thread.
	 */

	class SubmitListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			Thread downloader = new Thread(WebClient.this);
			downloader.start();
		}
	}

	/**
	 * Listener for Encode Data button. Open dialog to determine how to encode
	 * the data (name/value pairs or raw text).
	 */

	class EncodeListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			String queryData = getQueryData();
			if (queryData.length() == 0) {
				return;
			}
			JDialog dialog = new JDialog(frame, "Encode", true);
			dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			dialog.setLocationRelativeTo(frame);
			EncodeQueryPanel panel = new EncodeQueryPanel(dialog);
			dialog.getContentPane().add(panel);
			dialog.pack();
			dialog.setVisible(true);
			switch (panel.getValue()) {
			case JOptionPane.OK_OPTION:
				try {
					queryData = panel.encode(queryData);
					queryDataArea.setText(queryData);
				} catch (UnsupportedEncodingException uee) {
					report("Encoding problem: " + uee.getMessage());
				}
				break;
			case JOptionPane.CANCEL_OPTION:
				;
			default:
				;
			}
		}
	}

	/**
	 * Listener for Interrupt button. Stops download of Web page.
	 */

	class InterruptListener implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			client.setInterrupted(true);
		}
	}
}